package com.axapoint.opoc.core.dao;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.Cascade;
import org.joda.time.DateTime;
import org.joda.time.Interval;

import com.anasoft.os.daofusion.bitemporal.WrappedBitemporalProperty;
import com.anasoft.os.daofusion.bitemporal.WrappedValueAccessor;

@Entity
@Table(name = "PostalDistrict")
public class PostalDistrict extends ObjectRegistration<PostalDistrict, PostalDistrict.Value, PostalDistrict.Bitemporal> {
	private static final long serialVersionUID = 9168710745762860711L;

	@Entity
	@Table(name = "PostalDistrictValues")
	public static final class Value extends _Value<PostalDistrict, PostalDistrict.Bitemporal> {
		private static final long serialVersionUID = 1L;
		private String name;
		private int number;

		protected Value() {};
		Value(PostalDistrict pd, String systemId, String userId, Operation operation, String comment, Value value) {
			super(pd, systemId, userId, operation, comment);
			this.name = value.name;			
			this.number = value.number;			
		}
		public Value(int number, String name) {
			this.name = name;
			this.number = number;
		}
		public String getName() {
			return name;
		}
		protected void setName(String name) {
			this.name = name;
		}
		public int getNumber() {
			return number;
		}
		protected void setNumber(int number) {
			this.number = number;
		}
		public Location.Value[] getLocations() {
			ArrayList<Location.Value> result = new ArrayList<Location.Value>();
			
			Interval v = getBitemporal().getValidityInterval();
			for (Location.Value value : getEntity().getLocations()) {
				Interval ov = value.getBitemporal().getValidityInterval();
				
				// If any of the interval is within this values interval then include it 
				if ((ov.getStartMillis() >= v.getStartMillis() && ov.getStartMillis() < v.getEndMillis()) ||
					((ov.getEndMillis() >= v.getStartMillis() && ov.getEndMillis() < v.getEndMillis()))) {
					
					result.add(value);
				}
			}
			return result.toArray(new Location.Value[0]);
		}
		@Override
		public String diff(_Value<PostalDistrict, Bitemporal> otherValue, DateTime t0, DateTime t1) {
			StringBuffer sb = new StringBuffer();
			Value other = (Value)otherValue;

			if (!name.equals(other.name)) {
				sb.append("PostalDistrict.name: ").append(name).append(" - ").append(other.name).append('\n');
			}
			
			// List organizations that has changed
			Location.Value[] oT0 = getLocations();
			ArrayList<Location.Value> oT1 = new ArrayList<Location.Value>(Arrays.asList(other.getLocations()));

			for (Location.Value oV0 : oT0) {
				boolean found = false;
				for (Location.Value oV1 : oT1) {
					if (oV0.getEntity().getUuid().equals(oV1.getEntity().getUuid())) {
						oT1.remove(oV0);
						break;
					}
				}
				if (!found) {
					// Organization has been removed
					sb.append("PostalDistrict.location REMOVED: ").append(oV0.getEntity().getUuid()).append(" ").append(oV0.getAddress()).append('\n');
				}
			}
			for (Location.Value oV1 : oT1) {
				// Organization has been added
				sb.append("PostalDistrict.location ADDED: ").append(oV1.getEntity().getUuid()).append(" ").append(oV1.getAddress()).append('\n');
			}			
			
			return sb.toString();
		}
		@Override
		public String toString() {
			StringBuffer sb = new StringBuffer();
			sb.append(super.toString());
			sb.append("  name=").append(name).append('\n');
			sb.append("  number=").append(number).append('\n');

			int cnt = 0;
			for (Location.Value v : getLocations()) {
				if (v.getBitemporal().getValidityInterval().contains(getBitemporal().getValidityInterval().getStart())) {
					cnt++;
				}
			}
			sb.append("  #locations=").append(cnt).append('\n');
			
			return sb.toString();
		}
	}
	
	@Entity
	@Table(name = "PostalDistrictBitemporal")
	public static final class Bitemporal extends _Bitemporal<Bitemporal, Value> {
		private static final long serialVersionUID = -2132187534862370208L;
		
		protected Bitemporal() {}
	    private Bitemporal(PostalDistrict.Value value, Interval validityInterval) {
	        super(value, validityInterval);
	    }
	    
	    @Override
	    public Bitemporal copyWith(Interval validityInterval) {
	    	return new Bitemporal(value, validityInterval);
	    }
	}

	@SuppressWarnings("deprecation")
	@OneToMany(fetch = FetchType.LAZY)
    @Cascade(value = { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
    @JoinColumn(name="postalDistrictId")
    protected Collection<Location.Value> locations = new LinkedList<Location.Value>();	

	public PostalDistrict() {
		super();
	}
	public Collection<Location.Value> getLocations() {
		return locations;
	}
	protected void setLocations(Collection<Location.Value> locations) {
		this.locations = locations;
	}
    public WrappedBitemporalProperty<Value, Bitemporal> getValues() {
    	return new WrappedBitemporalProperty<Value, Bitemporal>(values, new WrappedValueAccessor<Value,Bitemporal>() {
			private static final long serialVersionUID = 1L;

			@Override
			public Bitemporal wrapValue(Value value, Interval validityInterval) {
				Bitemporal bi = new Bitemporal(value, validityInterval);
				return bi;
			}
    	});
    }
}
